<?php

/**
 * Description of EntityManager
 *
 * @author emanuele.colonnelli
 */

namespace org\phpstruct\db\ORM;

use org\phpstruct\core;
use org\phpstruct\db;
use org\phpstruct\db\I;

class EntityManager extends \org\phpstruct\json\JsonResponse {

    public static $typemap = array(
        'resource' => \PDO::PARAM_LOB,
        'integer' => \PDO::PARAM_INT,
        'string' => \PDO::PARAM_STR,
        'NULL' => \PDO::PARAM_NULL,
        'boolean' => \PDO::PARAM_BOOL
    );
    private static $db;

    public function __construct($db) {
        self::$db = $db;
    }

    public static function join(Entity $obj) {
        
    }

    public static function persist(Entity $obj) {

        $db = self::$db;
        try {
            $c = new \ReflectionObject($obj);
            $class = $c->getName();
            $table = $c->getConstant('ENTITY_TABLE');

            $dto = self::getInsertFieldList($obj);

            $db->beginTransaction();

            $q = sprintf("INSERT INTO %s (%s) VALUES (%s)", $table, $dto->getStrFields(), $dto->getStrValues());

            $stmt = $db->prepare($q);

            $fields = $dto->getFields();
            $values = $dto->getValues();
            for ($i = 0; $i < count($fields); $i++) {
//                $stmt->bindValue(":" . \org\phpstruct\util\StdLib::toDefaultCase($fields[$i]), $values[$i]);
                $stmt->bindValue(":" . $fields[$i], $values[$i]);
            }

            $stmt->execute();

            $ret = $db->lastInsertId();

            $db->commit();

            return $ret;
        } catch (\Exception $e) {
            $db->rollBack();
            throw $e;
        }
    }

    public static function update(Entity $obj, WhereClause $where) {

        $c = new \ReflectionObject($obj);
        $class = $c->getName();
        $table = $c->getConstant('ENTITY_TABLE');

        $dto = self::getUpdateFieldList($obj);

        $fields = $dto->getStrFields();
        $values = $dto->getStrValues();

        if (trim($fields) != "") {

            $db = self::$db;
            $q = sprintf("UPDATE %s SET %s %s", $table, $fields, "$where");

            $stmt = $db->prepare($q);

            $fields = $dto->getFields();
            $values = $dto->getValues();
            for ($i = 0; $i < count($fields); $i++) {
                self::bindValueHelper($stmt, ":" . $fields[$i], $values[$i]);
            }

            return $stmt->execute();
        } else {
            return false;
        }
    }

    private static function bindValueHelper(\PDOStatement $stmt, $key, $value) {
        $phptype = \gettype($value);
        $data_type = self::$typemap[$phptype];

        return $stmt->bindValue($key, $value, $data_type);
    }

    public static function delete(Entity $obj, WhereClause $where) {

        $c = new \ReflectionObject($obj);
        $class = $c->getName();
        $table = $c->getConstant('ENTITY_TABLE');

        $dto = self::getUpdateFieldList($obj);

        $db = self::$db;
        $q = sprintf("DELETE FROM %s %s", $table, "$where");

        $stmt = $db->prepare($q);

        $fields = $dto->getFields();
        $values = $dto->getValues();
        for ($i = 0; $i < count($fields); $i++) {
            $stmt->bindValue(":" . $fields[$i], $values[$i]);
        }

        return $stmt->execute();
    }

    /**
     *
     * @param Entity $obj
     * @return QueryFields
     */
    private static function getUpdateFieldList(Entity $obj) {

        $dto = self::entityProcess($obj);

        $fields = $dto->getFields();
        $values = $dto->getValues();
        $strFields = '';
        $sep = '';

        if (\is_array($fields) && \is_array($values) && (\count($fields) == \count($values))) {
            foreach ($fields as $field) {
                $strFields.= "{$sep}`{$field}` = :{$field}";
                $sep = ',';
            }
        }

        $dto->setStrFields($strFields);

        return $dto;
    }

    /**
     *
     * @param Entity $obj
     * @return QueryFields
     */
    private static function getInsertFieldList(Entity $obj) {

        $dto = self::entityProcess($obj);

        $fields = $dto->getFields();
        $values = $dto->getValues();
        $strFields = '';
        $strValues = '';
        $sep = '';

        if (\is_array($fields) && \is_array($values) && (\count($fields) == \count($values))) {
            foreach ($fields as $field) {
                $strFields.= "{$sep}`{$field}`";
                $strValues.= "{$sep}:{$field}";
                $sep = ',';
            }
        }

        $dto->setStrFields($strFields);
        $dto->setStrValues($strValues);

        return $dto;
    }

    /**
     *
     * @param entities_Category $obj
     * @return QueryFields
     */
    private static function entityProcess(Entity $obj) {
        $c = new \ReflectionObject($obj);
        $methods = $c->getMethods(\ReflectionMethod::IS_PUBLIC);
        $fields = array();
        $values = array();

        for ($i = 0; $i < count($methods); $i++) {

            if (\preg_match("/^get_(.*)/", $methods[$i]->name, $ret)) {
                $val = $methods[$i]->invoke($obj, null);
                if (!is_null($val)) {
                    \array_push($fields, $ret[1]);
                    \array_push($values, $val);
                }
            }
        }

        $ret = new QueryFields();
        $ret->setFields($fields);
        $ret->setValues($values);

        return $ret;
    }

    public function findAll(Entity $ent, WhereClause $where) {

        try {

            $db = self::$db;

            $c = new \ReflectionObject($obj);
            $class = $c->getName();
            $table = $c->getConstant('ENTITY_TABLE');

            $q = "SELECT * FROM {$table} {$where}";

            $rs = $db->query($q);
            $rows = array();
            while($row = $rs->fetchObject()){
                \array_push($rows, $row);
            }

            return $rows;

        } catch (Exception $exc) {
            echo $exc->getTraceAsString();
        }
    }
}
